home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Jumpstart / Multimedia Microsoft Jumpstart Version 1.1a (Microsoft).BIN / develpmt / sdk / vfw11.win / vfwdk / codec1.c_ / codec1.bin
Encoding:
Text File  |  1993-11-19  |  79.0 KB  |  2,413 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992, 1993  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11. //
  12. //  codec.c
  13. //
  14. //  Description:
  15. //
  16. //
  17. //
  18. //==========================================================================;
  19.  
  20. #include <windows.h>
  21. #include <windowsx.h>
  22. #include <mmsystem.h>
  23. #include <mmreg.h>
  24. #include <msacm.h>
  25. #include <msacmdrv.h>
  26. #include <memory.h>
  27.  
  28. #include "codec.h"
  29. #include "msfilter.h"
  30.  
  31. #include "debug.h"
  32.  
  33.  
  34. //
  35. //  array of WAVE format tags supported.
  36. //
  37. //  NOTE! if you change anything in this structure (order, addition, removal)
  38. //  you must also fix acmdFormatTagDetails!
  39. //
  40. static UINT gauFormatTagIndexToTag[] =
  41. {
  42.     WAVE_FORMAT_PCM
  43. };
  44.  
  45. #define ACM_DRIVER_MAX_FORMAT_TAGS  SIZEOF_ARRAY(gauFormatTagIndexToTag)
  46.  
  47.  
  48. //
  49. //  array of _standard_ sample rates supported
  50. //
  51. //
  52. static UINT gauFormatIndexToSampleRate[] =
  53. {
  54.     8000,
  55.     11025,
  56.     22050,
  57.     44100
  58. };
  59.  
  60. #define ACM_DRIVER_MAX_SAMPLE_RATES SIZEOF_ARRAY(gauFormatIndexToSampleRate)
  61.  
  62. //
  63. //
  64. //
  65. //
  66. #define ACM_DRIVER_MAX_CHANNELS     (MSFILTER_MAX_CHANNELS)
  67.  
  68.  
  69. //
  70. //  array of bits per sample supported
  71. //
  72. //
  73. static UINT gauFormatIndexToBitsPerSample[] =
  74. {
  75.     8,
  76.     16
  77. };
  78.  
  79. #define ACM_DRIVER_MAX_BITSPERSAMPLE_PCM    SIZEOF_ARRAY(gauFormatIndexToBitsPerSample)
  80.  
  81.  
  82. //
  83. //  number of formats we enumerate per channel is number of sample rates
  84. //  times number of channels times number of types (bits per sample).
  85. //
  86. #define ACM_DRIVER_MAX_STANDARD_FORMATS_PCM (ACM_DRIVER_MAX_SAMPLE_RATES *  \
  87.                                              ACM_DRIVER_MAX_CHANNELS *      \
  88.                                              ACM_DRIVER_MAX_BITSPERSAMPLE_PCM)
  89.  
  90.  
  91. //
  92. //  Array of WAVE filter tags supported.
  93. //
  94. static DWORD gadwFilterTagIndexToTag[] =
  95. {
  96.     WAVE_FILTER_VOLUME,
  97.     WAVE_FILTER_ECHO
  98. };
  99.  
  100. #define ACM_DRIVER_MAX_FILTER_TAGS          SIZEOF_ARRAY(gadwFilterTagIndexToTag)
  101.  
  102.  
  103. //
  104. //  Array of filters supported.
  105. //
  106. static DWORD gdwFilterIndexToVolume[] =
  107. {
  108.     0x00001000,
  109.     0x00002000,
  110.     0x00004000,
  111.     0x00006000,
  112.     0x00008000,
  113.     0x0000A000,
  114.     0x0000C000,
  115.     0x0000E000,
  116.     0x0000F000,
  117.     0x00011000,
  118.     0x00012000,
  119.     0x00014000,
  120.     0x00016000,
  121.     0x00018000,
  122.     0x0001A000,
  123.     0x0001C000,
  124.     0x00020000
  125. };
  126. #define ACM_DRIVER_MAX_VOLUME_FILTERS   SIZEOF_ARRAY(gdwFilterIndexToVolume)
  127.  
  128. static DWORD gdwFilterIndexToDelay[] =
  129. {
  130.     0x00000040,
  131.     0x00000080,
  132.     0x00000100,
  133.     0x00000180,
  134.     0x00000200,
  135.     0x00000300,
  136.     0x00000400,
  137.     0x00000800,
  138. };
  139. #define ACM_DRIVER_NUM_DELAY            SIZEOF_ARRAY(gdwFilterIndexToDelay)
  140.  
  141. static DWORD gdwFilterIndexToEchoVol[] =
  142. {
  143.     0x00001000,
  144.     0x00002000,
  145.     0x00004000,
  146.     0x00008000,
  147.     0x0000C000
  148. };
  149. #define ACM_DRIVER_NUM_ECHOVOL          SIZEOF_ARRAY(gdwFilterIndexToEchoVol)
  150.  
  151. #define ACM_DRIVER_MAX_ECHO_FILTERS     (ACM_DRIVER_NUM_DELAY *     \
  152.                                          ACM_DRIVER_NUM_ECHOVOL)
  153.  
  154.  
  155. //
  156. //
  157. //
  158. //
  159. TCHAR BCODE     gszIniSystem[]  = TEXT("system.ini");
  160. TCHAR BCODE     gszKeyConfig[]  = TEXT("Config");
  161.  
  162.  
  163. //==========================================================================;
  164. //
  165. //
  166. //
  167. //
  168. //==========================================================================;
  169.  
  170. //--------------------------------------------------------------------------;
  171. //
  172. //  BOOL pcmIsValidFormat
  173. //
  174. //  Description:
  175. //      This function verifies that a wave format header is a valid PCM
  176. //      header that _this_ ACM driver can deal with.
  177. //
  178. //  Arguments:
  179. //      LPWAVEFORMATEX pwfx: Pointer to format header to verify.
  180. //
  181. //  Return (BOOL):
  182. //      The return value is non-zero if the format header looks valid. A
  183. //      zero return means the header is not valid.
  184. //
  185. //--------------------------------------------------------------------------;
  186.  
  187. BOOL FNLOCAL pcmIsValidFormat
  188. (
  189.     LPWAVEFORMATEX  pwfx
  190. )
  191. {
  192.     if (NULL == pwfx)
  193.         return (FALSE);
  194.  
  195.     if (WAVE_FORMAT_PCM != pwfx->wFormatTag)
  196.         return (FALSE);
  197.  
  198.     //
  199.     //  verify nChannels member is within the allowed range
  200.     //
  201.     if ((pwfx->nChannels < 1) || (pwfx->nChannels > ACM_DRIVER_MAX_CHANNELS))
  202.         return (FALSE);
  203.  
  204.     //
  205.     //  only allow the bits per sample that we can encode and decode with
  206.     //
  207.     if ((8 != pwfx->wBitsPerSample) && (16 != pwfx->wBitsPerSample))
  208.         return (FALSE);
  209.  
  210.     //
  211.     //  now verify that the block alignment is correct..
  212.     //
  213.     if (PCM_BLOCKALIGNMENT(pwfx) != pwfx->nBlockAlign)
  214.         return (FALSE);
  215.  
  216.     //
  217.     //  verify samples per second is within our capabilities
  218.     //
  219.     if ((0L == pwfx->nSamplesPerSec) || (0x3FFFFFFF < pwfx->nSamplesPerSec))
  220.     {
  221.         return (FALSE);
  222.     }
  223.  
  224.     //
  225.     //  finally, verify that avg bytes per second is correct
  226.     //
  227.     if (PCM_AVGBYTESPERSEC(pwfx) != pwfx->nAvgBytesPerSec)
  228.         return (FALSE);
  229.  
  230.     return (TRUE);
  231. } // pcmIsValidFormat()
  232.  
  233.  
  234. //--------------------------------------------------------------------------;
  235. //
  236. //  BOOL volumeIsValidFilter
  237. //
  238. //  Description:
  239. //      This function verifies that a wave filter header is a valid volume
  240. //      header that our volume converter can deal with.
  241. //
  242. //  Arguments:
  243. //      LPWAVEFILTER pwf: Pointer to filter header to verify.
  244. //
  245. //  Return (BOOL):
  246. //      The return value is non-zero if the format header looks valid. A
  247. //      zero return means the header is not valid.
  248. //
  249. //  History:
  250. //      06/05/93    Created. 
  251. //
  252. //--------------------------------------------------------------------------;
  253.  
  254. BOOL FNLOCAL volumeIsValidFilter
  255. (
  256.     LPWAVEFILTER  pwf
  257. )
  258. {
  259.     if (!pwf)
  260.         return (FALSE);
  261.  
  262.     if (pwf->cbStruct < sizeof(VOLUMEWAVEFILTER))
  263.         return (FALSE);
  264.  
  265.     if (pwf->dwFilterTag != WAVE_FILTER_VOLUME)
  266.         return (FALSE);
  267.  
  268.     if (0L != pwf->fdwFilter)
  269.         return (FALSE);
  270.  
  271.     return (TRUE);
  272. } // volumeIsValidFilter()
  273.  
  274.  
  275.  
  276. //--------------------------------------------------------------------------;
  277. //
  278. //  BOOL echoIsValidFilter
  279. //
  280. //  Description:
  281. //      This function verifies that a wave filter header is a valid echo
  282. //      header that our echo converter can deal with.
  283. //
  284. //  Arguments:
  285. //      LPWAVEFILTER pwf: Pointer to filter header to verify.
  286. //
  287. //  Return (BOOL):
  288. //      The return value is non-zero if the format header looks valid. A
  289. //      zero return means the header is not valid.
  290. //
  291. //  History:
  292. //      06/05/93    Created. 
  293. //
  294. //--------------------------------------------------------------------------;
  295.  
  296. BOOL FNLOCAL echoIsValidFilter
  297. (
  298.     LPWAVEFILTER  pwf
  299. )
  300. {
  301.     LPECHOWAVEFILTER    pwfEcho;
  302.     
  303.     if (!pwf)
  304.         return (FALSE);
  305.  
  306.     if (pwf->cbStruct < sizeof(ECHOWAVEFILTER))
  307.         return (FALSE);
  308.  
  309.     if (pwf->dwFilterTag != WAVE_FILTER_ECHO)
  310.         return (FALSE);
  311.  
  312.     if (0L != pwf->fdwFilter)
  313.         return (FALSE);
  314.  
  315.     pwfEcho = (LPECHOWAVEFILTER)pwf;
  316.     // We only support a delay value up to 10 sec or 10 000 msec.
  317.     if (pwfEcho->dwDelay > 10000L)
  318.         return (FALSE);
  319.  
  320.     return (TRUE);
  321. } // echoIsValidFilter()
  322.  
  323.  
  324. //==========================================================================;
  325. //
  326. //
  327. //
  328. //
  329. //==========================================================================;
  330.  
  331.  
  332. //--------------------------------------------------------------------------;
  333. //
  334. //  LRESULT acmdDriverOpen
  335. //
  336. //  Description:
  337. //      This function is used to handle the DRV_OPEN message for the ACM
  338. //      driver. The driver is 'opened' for many reasons with the most common
  339. //      being in preperation for conversion work. It is very important that
  340. //      the driver be able to correctly handle multiple open driver
  341. //      instances.
  342. //
  343. //      Read the comments for this function carefully!
  344. //
  345. //      Note that multiple _streams_ can (and will) be opened on a single
  346. //      open _driver instance_. Do not store/create instance data that must
  347. //      be unique for each stream in this function. See the acmdStreamOpen
  348. //      function for information on conversion streams.
  349. //
  350. //  Arguments:
  351. //      HDRVR hdrvr: Driver handle that will be returned to caller of the
  352. //      OpenDriver function. Normally, this will be the ACM--but this is
  353. //      not guaranteed. For example, if an ACM driver is implemented within
  354. //      a waveform driver, then the driver will be opened by both MMSYSTEM
  355. //      and the ACM.
  356. //
  357. //      LPACMDRVOPENDESC paod: Open description defining how the ACM driver
  358. //      is being opened. This argument may be NULL--see the comments below
  359. //      for more information.
  360. //
  361. //  Return (LRESULT):
  362. //      The return value is non-zero if the open is successful. A zero
  363. //      return signifies that the driver cannot be opened.
  364. //
  365. //--------------------------------------------------------------------------;
  366.  
  367. LRESULT FNLOCAL acmdDriverOpen
  368. (
  369.     HDRVR                   hdrvr,
  370.     LPACMDRVOPENDESC        paod
  371. )
  372. {
  373.     PDRIVERINSTANCE     pdi;
  374.  
  375.     //
  376.     //  the [optional] open description that is passed to this driver can
  377.     //  be from multiple 'managers.' for example, AVI looks for installable
  378.     //  drivers that are tagged with 'vidc' and 'vcap'. we need to verify
  379.     //  that we are being opened as an Audio Compression Manager driver.
  380.     //
  381.     //  if paod is NULL, then the driver is being opened for some purpose
  382.     //  other than converting (that is, there will be no stream open
  383.     //  requests for this instance of being opened). the most common case
  384.     //  of this is the Control Panel's Drivers option checking for config
  385.     //  support (DRV_[QUERY]CONFIGURE).
  386.     //
  387.     //  we want to succeed this open, but be able to know that this
  388.     //  open instance is bogus for creating streams. for this purpose we
  389.     //  leave most of the members of our instance structure that we 
  390.     //  allocate below as zero...
  391.     //
  392.     if (NULL != paod)
  393.     {
  394.         //
  395.         //  refuse to open if we are not being opened as an ACM driver.
  396.         //  note that we do NOT modify the value of paod->dwError in this
  397.         //  case.
  398.         //
  399.         if (ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC != paod->fccType)
  400.         {
  401.             return (0L);
  402.         }
  403.     }
  404.  
  405.  
  406.     //
  407.     //  we are being opened as an installable driver--we can allocate some
  408.     //  instance data to be returned in dwId argument of the DriverProc;
  409.     //  or simply return non-zero to succeed the open.
  410.     //
  411.     //  this driver allocates a small instance structure. note that we
  412.     //  rely on allocating the memory as zero-initialized!
  413.     //
  414.     pdi = (PDRIVERINSTANCE)LocalAlloc(LPTR, sizeof(*pdi));
  415.     if (NULL == pdi)
  416.     {
  417.         //
  418.         //  if this open attempt was as an ACM driver, then return the
  419.         //  reason we are failing in the open description structure..
  420.         //
  421.         if (NULL != paod)
  422.         {
  423.             paod->dwError = MMSYSERR_NOMEM;
  424.         }
  425.  
  426.         //
  427.         //  fail to open
  428.         //
  429.         return (0L);
  430.     }
  431.  
  432.  
  433.     //
  434.     //  fill in our instance structure... note that this instance data
  435.     //  can be anything that the ACM driver wishes to maintain the
  436.     //  open driver instance. this data should not contain any information
  437.     //  that must be maintained per open stream since multiple streams
  438.     //  can be opened on a single driver instance.
  439.     //
  440.     //  also note that we do _not_ check the version of the ACM opening
  441.     //  us (paod->dwVersion) to see if it is at least new enough to work
  442.     //  with this driver (for example, if this driver required Version 3.0
  443.     //  of the ACM and a Version 2.0 installation tried to open us). the
  444.     //  reason we do not fail is to allow the ACM to get the driver details
  445.     //  which contains the version of the ACM that is _required_ by this
  446.     //  driver. the ACM will examine that value (in padd->vdwACM) and
  447.     //  do the right thing for this driver... like not load it and inform
  448.     //  the user of the problem.
  449.     //
  450.     pdi->hdrvr     = hdrvr;
  451.  
  452.  
  453.     if (NULL != paod)
  454.     {
  455.         pdi->fnDriverProc = NULL;
  456.         pdi->fccType      = paod->fccType;
  457.         pdi->vdwACM       = paod->dwVersion;
  458.         pdi->fdwOpen      = paod->dwFlags;
  459.  
  460. #if 0
  461.         pdi->fdwConfig    = GetPrivateProfileInt(paod->pszAliasName,
  462.                                                  gszKeyConfig,
  463.                                                  MSFILTER_CONFIG_DEFAULT,
  464.                                                  gszIniSystem);
  465. #endif
  466.  
  467.         paod->dwError     = MMSYSERR_NOERROR;
  468.     }
  469.  
  470.  
  471.     //
  472.     //  non-zero return is success for DRV_OPEN
  473.     //
  474.     return ((LRESULT)(UINT)pdi);
  475. } // acmdDriverOpen()
  476.  
  477.  
  478. //--------------------------------------------------------------------------;
  479. //
  480. //  LRESULT acmdDriverClose
  481. //
  482. //  Description:
  483. //      This function handles the DRV_CLOSE message for the ACM driver. The
  484. //      driver receives a DRV_CLOSE message for each succeeded DRV_OPEN
  485. //      message (see acmdDriverOpen). The driver will only receive a close
  486. //      message for _successful_ opens.
  487. //
  488. //  Arguments:
  489. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  490. //      This structure is [optionally] allocated during the DRV_OPEN message
  491. //      which is handled by the acmdDriverOpen function.
  492. //
  493. //  Return (LRESULT):
  494. //      The return value is non-zero if the open instance can be closed.
  495. //      A zero return signifies that the ACM driver instance could not be
  496. //      closed.
  497. //
  498. //      NOTE! It is _strongly_ recommended that the driver never fail to
  499. //      close. Note that the ACM will never allow a driver instance to
  500. //      be closed if there are open streams. An ACM driver does not need
  501. //      to check for this case.
  502. //
  503. //--------------------------------------------------------------------------;
  504.  
  505. LRESULT FNLOCAL acmdDriverClose
  506. (
  507.     PDRIVERINSTANCE         pdi
  508. )
  509. {
  510.     //
  511.     //  check to see if we allocated instance data. if we did not, then
  512.     //  immediately succeed.
  513.     //
  514.     if (NULL != pdi)
  515.     {
  516.         //
  517.         //  close down the driver instance. this driver simply needs
  518.         //  to free the instance data structure... note that if this 
  519.         //  'free' fails, then this ACM driver probably trashed its
  520.         //  heap; assume we didn't do that.
  521.         //
  522.         LocalFree((HLOCAL)pdi);
  523.     }
  524.  
  525.  
  526.     //
  527.     //  non-zero return is success for DRV_CLOSE
  528.     //
  529.     return (1L);
  530. } // acmdDriverClose()
  531.  
  532.  
  533. //--------------------------------------------------------------------------;
  534. //
  535. //  LRESULT acmdDriverConfigure
  536. //
  537. //  Description:
  538. //      This function is called to handle the DRV_[QUERY]CONFIGURE messages.
  539. //      These messages are for 'configuration' support of the driver.
  540. //      Normally this will be for 'hardware'--that is, a dialog should be
  541. //      displayed to configure ports, IRQ's, memory mappings, etc if it
  542. //      needs to. However, a software only ACM driver may also require
  543. //      configuration for 'what is real time' or other quality vs time
  544. //      issues.
  545. //
  546. //      The most common way that these messages are generated under Win 3.1
  547. //      and NT Product 1 is from the Control Panel's Drivers option. Other
  548. //      sources may generate these messages in future versions of Windows.
  549. //
  550. //  Arguments:
  551. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  552. //      This structure is [optionally] allocated during the DRV_OPEN message
  553. //      which is handled by the acmdDriverOpen function.
  554. //
  555. //      HWND hwnd: Handle to parent window to use when displaying the
  556. //      configuration dialog box. An ACM driver is _required_ to display a
  557. //      modal dialog box using this hwnd argument as the parent. This
  558. //      argument may be (HWND)-1 which tells the driver that it is only
  559. //      being queried for configuration support.
  560. //
  561. //      LPDRVCONFIGINFO pdci: Pointer to optional DRVCONFIGINFO structure.
  562. //      If this argument is NULL, then the ACM driver should invent its own
  563. //      storage location.
  564. //
  565. //  Return (LRESULT):
  566. //      If the driver is being 'queried' for configuration support (that is,
  567. //      hwnd == (HWND)-1), then non-zero should be returned specifying
  568. //      the driver does support a configuration dialog--or zero should be
  569. //      returned specifying that no configuration dialog is supported.
  570. //
  571. //      If the driver is being called to display the configuration dialog
  572. //      (that is, hwnd != (HWND)-1), then one of the following values
  573. //      should be returned:
  574. //
  575. //      DRVCNF_CANCEL (0x0000): specifies that the dialog was displayed
  576. //      and canceled by the user. this value should also be returned if
  577. //      no configuration information was modified.
  578. //
  579. //      DRVCNF_OK (0x0001): specifies that the dialog was displayed and
  580. //      some configuration information was changed. however, the driver
  581. //      does not require Windows to be restarted--the changes have already
  582. //      been applied.
  583. //
  584. //      DRVCNF_RESTART (0x0002): specifies that the dialog was displayed
  585. //      and some configuration information was changed that requires 
  586. //      Windows to be restarted before the changes take affect. the driver
  587. //      should remain configured with current values until the driver
  588. //      has been 'rebooted'.
  589. //
  590. //--------------------------------------------------------------------------;
  591.  
  592. LRESULT FNLOCAL acmdDriverConfigure
  593. (
  594.     PDRIVERINSTANCE         pdi,
  595.     HWND                    hwnd,
  596.     LPDRVCONFIGINFO         pdci
  597. )
  598. {
  599.     //
  600.     //  first check to see if we are only being queried for configuration
  601.     //  support. if hwnd == (HWND)-1 then we are being queried and should
  602.     //  return zero for 'not supported' and non-zero for 'supported'.
  603.     //
  604.     if ((HWND)-1 == hwnd)
  605.     {
  606.         //
  607.         //  this ACM driver does not support a configuration dialog box, so
  608.         //  return zero...
  609.         //
  610.         return (0L);
  611.     }
  612.  
  613.  
  614.     //
  615.     //  we are being asked to bring up our configuration dialog. if this
  616.     //  driver supports a configuration dialog box, then after the dialog
  617.     //  is dismissed we must return one of the following values:
  618.     //
  619.     //  DRVCNF_CANCEL (0x0000): specifies that the dialog was displayed
  620.     //  and canceled by the user. this value should also be returned if
  621.     //  no configuration information was modified.
  622.     //
  623.     //  DRVCNF_OK (0x0001): specifies that the dialog was displayed and
  624.     //  some configuration information was changed. however, the driver
  625.     //  does not require Windows to be restarted--the changes have already
  626.     //  been applied.
  627.     //
  628.     //  DRVCNF_RESTART (0x0002): specifies that the dialog was displayed
  629.     //  and some configuration information was changed that requires 
  630.     //  Windows to be restarted before the changes take affect. the driver
  631.     //  should remain configured with current values until the driver
  632.     //  has been 'rebooted'.
  633.     //
  634.     //
  635.     if (NULL == pdci)
  636.     {
  637.         //
  638.         //  !!!
  639.         //
  640.         return (DRVCNF_CANCEL);
  641.     }
  642.  
  643. #if 0
  644.     int         n;
  645.  
  646.     pdi->pdci = pdci;
  647.  
  648.     n = DialogBoxParam(ghinst,
  649.                        DLG_CONFIGURATION,
  650.                        hwnd,
  651.                        msfilterDlgProcConfigure,
  652.                        (LPARAM)(UINT)pdi);
  653.  
  654.     pdi->pdci = NULL;
  655.  
  656.     return ((LRESULT)n);
  657. #else
  658.     return (DRVCNF_CANCEL);
  659. #endif
  660. } // acmdDriverConfigure()
  661.  
  662.  
  663. //--------------------------------------------------------------------------;
  664. //
  665. //  LRESULT acmdDriverDetails
  666. //
  667. //  Description:
  668. //      This function handles the ACMDM_DRIVER_DETAILS message. The ACM
  669. //      driver is responsible for filling in the ACMDRIVERDETAILS structure
  670. //      with various information.
  671. //
  672. //      NOTE! It is *VERY* important that you fill in your ACMDRIVERDETAILS
  673. //      structure correctly. The ACM and applications must be able to 
  674. //      rely on this information.
  675. //
  676. //      WARNING! The _reserved_ bits of any fields of the ACMDRIVERDETAILS
  677. //      structure are _exactly that_: RESERVED. Do NOT use any extra
  678. //      flag bits, etc. for custom information. The proper way to add
  679. //      custom capabilities to your ACM driver is this:
  680. //
  681. //      o   define a new message in the ACMDM_USER range.
  682. //
  683. //      o   an application that wishes to use one of these extra features
  684. //          should then:
  685. //
  686. //          o   open the driver with acmDriverOpen.
  687. //
  688. //          o   check for the proper wMid and wPid using acmDriverDetails.
  689. //
  690. //          o   send the 'user defined' message with acmDriverMessage
  691. //              to retrieve additional information, etc.
  692. //
  693. //          o   close the driver with acmDriverClose.
  694. //
  695. //  Arguments:
  696. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  697. //      This structure is [optionally] allocated during the DRV_OPEN message
  698. //      which is handled by the acmdDriverOpen function.
  699. //
  700. //      LPACMDRIVERDETAILS padd: Pointer to ACMDRIVERDETAILS structure to
  701. //      fill in for the caller. This structure may be larger or smaller than
  702. //      the current definition of ACMDRIVERDETAILS--cbStruct specifies the
  703. //      valid size.
  704. //
  705. //  Return (LRESULT):
  706. //      The return value is zero (MMSYSERR_NOERROR) for success. Non-zero
  707. //      signifies that the driver details could not be retrieved.
  708. //
  709. //      NOTE THAT THIS FUNCTION SHOULD NEVER FAIL! There are two possible
  710. //      error conditions:
  711. //
  712. //      o   if padd is NULL or an invalid pointer.
  713. //
  714. //      o   if cbStruct is less than four; in this case, there is not enough
  715. //          room to return the number of bytes filled in.
  716. //
  717. //      Because these two error conditions are easily defined, the ACM
  718. //      will catch these errors. The driver does NOT need to check for these
  719. //      conditions.
  720. //
  721. //--------------------------------------------------------------------------;
  722.  
  723. LRESULT FNLOCAL acmdDriverDetails
  724. (
  725.     PDRIVERINSTANCE         pdi,
  726.     LPACMDRIVERDETAILS      padd
  727. )
  728. {
  729.     ACMDRIVERDETAILS    add;
  730.     DWORD               cbStruct;
  731.  
  732.     //
  733.     //  it is easiest to fill in a temporary structure with valid info
  734.     //  and then copy the requested number of bytes to the destination
  735.     //  buffer.
  736.     //
  737.     cbStruct            = min(padd->cbStruct, sizeof(ACMDRIVERDETAILS));
  738.     add.cbStruct        = cbStruct;
  739.  
  740.  
  741.     //
  742.     //  for the current implementation of an ACM driver, the fccType and
  743.     //  fccComp members *MUST* always be ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC
  744.     //  ('audc') and ACMDRIVERDETAILS_FCCCOMP_UNDEFINED (0) respectively.
  745.     //
  746.     add.fccType         = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
  747.     add.fccComp         = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
  748.  
  749.  
  750.     //
  751.     //  the manufacturer id (wMid) and product id (wPid) must be filled
  752.     //  in with your company's _registered_ identifier's. for more
  753.     //  information on these identifier's and how to get them registered
  754.     //  contact Microsoft and get the Multimedia Developer Registration Kit:
  755.     //
  756.     //      Microsoft Corporation
  757.     //      Multimedia Technology Group
  758.     //      One Microsoft Way
  759.     //      Redmond, WA 98052-6399
  760.     //
  761.     //      Developer Services Phone: (800) 227-4679 x11771
  762.     //
  763.     //  note that during the development phase or your ACM driver, you may
  764.     //  use the reserved value of '0' for both wMid and wPid. however it
  765.     //  is not acceptable to ship a driver with these values.
  766.     //
  767.     add.wMid            = MM_MICROSOFT;
  768.     add.wPid            = MM_MSFT_ACM_MSFILTER;
  769.  
  770.  
  771.     //
  772.     //  the vdwACM and vdwDriver members contain version information for
  773.     //  the driver.
  774.     //
  775.     //  vdwACM: must contain the version of the *ACM* that the driver was
  776.     //  _designed_ for. this is the _minimum_ version number of the ACM
  777.     //  that the driver will work with. this value must be >= V2.00.000.
  778.     //
  779.     //  vdwDriver: the version of this ACM driver.
  780.     //
  781.     //  ACM driver versions are 32 bit numbers broken into three parts as
  782.     //  follows (note these parts are displayed as decimal values):
  783.     //
  784.     //      bits 24 - 31:   8 bit _major_ version number
  785.     //      bits 16 - 23:   8 bit _minor_ version number
  786.     //      bits  0 - 15:   16 bit build number
  787.     //
  788.     add.vdwACM          = VERSION_MSACM;
  789.     add.vdwDriver       = VERSION_ACM_DRIVER;
  790.  
  791.  
  792.     //
  793.     //  the following flags are used to specify the type of conversion(s)
  794.     //  that the ACM driver supports. note that a driver may support one or
  795.     //  more of these flags in any combination.
  796.     //
  797.     //  ACMDRIVERDETAILS_SUPPORTF_CODEC: this flag is set if the driver 
  798.     //  supports conversions from one format tag to another format tag. for
  799.     //  example, if a converter compresses or decompresses WAVE_FORMAT_PCM
  800.     //  and WAVE_FORMAT_IMA_ADPCM, then this bit should be set. this is
  801.     //  true even if the data is not actually changed in size--for example
  802.     //  a conversion from u-Law to A-Law will still set this bit because
  803.     //  the format tags differ.
  804.     //
  805.     //  ACMDRIVERDETAILS_SUPPORTF_CONVERTER: this flags is set if the
  806.     //  driver supports conversions on the same format tag. as an example,
  807.     //  the PCM converter that is built into the ACM sets this bit (and only
  808.     //  this bit) because it converts only between PCM formats (bits, sample
  809.     //  rate).
  810.     //
  811.     //  ACMDRIVERDETAILS_SUPPORTF_FILTER: this flag is set if the driver 
  812.     //  supports transformations on a single format tag but does change
  813.     //  the base characteristics of the format (bit depth, sample rate, etc
  814.     //  will remain the same). for example, a driver that changed the
  815.     //  'volume' of PCM data or applied a low pass filter would set this bit.
  816.     //
  817.     add.fdwSupport      = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  818.  
  819.  
  820.     //
  821.     //  the number of individual format tags this ACM driver supports. for
  822.     //  example, if a driver uses the WAVE_FORMAT_IMA_ADPCM and
  823.     //  WAVE_FORMAT_PCM format tags, then this value would be two. if the
  824.     //  driver only supports filtering on WAVE_FORMAT_PCM, then this value
  825.     //  would be one. if this driver supported WAVE_FORMAT_ALAW,
  826.     //  WAVE_FORMAT_MULAW and WAVE_FORMAT_PCM, then this value would be
  827.     //  three. etc, etc.
  828.     //
  829.     add.cFormatTags     = ACM_DRIVER_MAX_FORMAT_TAGS;
  830.  
  831.     //
  832.     //  the number of individual filter tags this ACM driver supports. if
  833.     //  a driver supports no filters (ACMDRIVERDETAILS_SUPPORTF_FILTER is
  834.     //  NOT set in the fdwSupport member), then this value must be zero.
  835.     //
  836.     add.cFilterTags     = ACM_DRIVER_MAX_FILTER_TAGS;
  837.  
  838.  
  839.     //
  840.     //  the remaining members in the ACMDRIVERDETAILS structure are sometimes
  841.     //  not needed. because of this we make a quick check to see if we
  842.     //  should go through the effort of filling in these members.
  843.     //
  844.     if (FIELD_OFFSET(ACMDRIVERDETAILS, hicon) < cbStruct)
  845.     {
  846.         //
  847.         //  fill in the hicon member will a handle to a custom icon for
  848.         //  the ACM driver. this allows the driver to be represented by
  849.         //  an application graphically (usually this will be a company
  850.         //  logo or something). if a driver does not wish to have a custom
  851.         //  icon displayed, then simply set this member to NULL and a
  852.         //  generic icon will be displayed instead.
  853.         //
  854. #ifdef DEBUG
  855.         add.hicon = LoadIcon(ghinst, ICON_ACM_DRIVER);  // NULL
  856. #else
  857.         add.hicon = NULL;
  858. #endif
  859.  
  860.         //
  861.         //  the short name and long name are used to represent the driver
  862.         //  in a unique description. the short name is intended for small
  863.         //  display areas (for example, in a menu or combo box). the long
  864.         //  name is intended for more descriptive displays (for example,
  865.         //  in an 'about box').
  866.         //
  867.         //  NOTE! an ACM driver should never place formatting characters
  868.         //  of any sort in these strings (for example CR/LF's, etc). it
  869.         //  is up to the application to format the text.
  870.         //
  871.         LoadString(ghinst, IDS_ACM_DRIVER_SHORTNAME, add.szShortName, SIZEOF(add.szShortName));
  872.         LoadString(ghinst, IDS_ACM_DRIVER_LONGNAME,  add.szLongName,  SIZEOF(add.szLongName));
  873.  
  874.         //
  875.         //  the last three members are intended for 'about box' information.
  876.         //  these members are optional and may be zero length strings if
  877.         //  the driver wishes.
  878.         //
  879.         //  NOTE! an ACM driver should never place formatting characters
  880.         //  of any sort in these strings (for example CR/LF's, etc). it
  881.         //  is up to the application to format the text.
  882.         //
  883.         if (FIELD_OFFSET(ACMDRIVERDETAILS, szCopyright) < cbStruct)
  884.         {
  885.             LoadString(ghinst, IDS_ACM_DRIVER_COPYRIGHT, add.szCopyright, SIZEOF(add.szCopyright));
  886.             LoadString(ghinst, IDS_ACM_DRIVER_LICENSING, add.szLicensing, SIZEOF(add.szLicensing));
  887.             LoadString(ghinst, IDS_ACM_DRIVER_FEATURES,  add.szFeatures,  SIZEOF(add.szFeatures));
  888.         }
  889.     }
  890.  
  891.  
  892.     //
  893.     //  now copy the correct number of bytes to the caller's buffer
  894.     //
  895.     _fmemcpy(padd, &add, (UINT)add.cbStruct);
  896.  
  897.  
  898.     //
  899.     //  success!
  900.     //
  901.     return (MMSYSERR_NOERROR);
  902. } // acmdDriverDetails()
  903.  
  904.  
  905. //--------------------------------------------------------------------------;
  906. //
  907. //  LRESULT acmdDriverAbout
  908. //
  909. //  Description:
  910. //      This function is called to handle the ACMDM_DRIVER_ABOUT message.
  911. //      An ACM driver has the option of displaying its own 'about box' or
  912. //      letting the ACM (or calling application) display one for it. This
  913. //      message is normally sent by the Control Panel's Sound Mapper
  914. //      option.
  915. //
  916. //      It is recommended that an ACM driver allow a default about box
  917. //      be displayed for it--there should be no reason to bloat the size
  918. //      of a driver to simply display copyright, etc information when that
  919. //      information is contained in the ACMDRIVERDETAILS structure.
  920. //
  921. //  Arguments:
  922. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  923. //      This structure is [optionally] allocated during the DRV_OPEN message
  924. //      which is handled by the acmdDriverOpen function.
  925. //
  926. //      HWND hwnd: Handle to parent window to use when displaying the
  927. //      configuration dialog box. An ACM driver is _required_ to display a
  928. //      modal dialog box using this hwnd argument as the parent. This
  929. //      argument may be (HWND)-1 which tells the driver that it is only
  930. //      being queried for about box support.
  931. //
  932. //  Return (LRESULT):
  933. //      The return value is MMSYSERR_NOTSUPPORTED if the ACM driver does
  934. //      not support a custom dialog box. In this case, the ACM or calling
  935. //      application will display a generic about box using the information
  936. //      contained in the ACMDRIVERDETAILS structure returned by the
  937. //      ACMDM_DRIVER_DETAILS message.
  938. //
  939. //      If the driver chooses to display its own dialog box, then after
  940. //      the dialog is dismissed by the user, MMSYSERR_NOERROR should be
  941. //      returned.
  942. //
  943. //      If the hwnd argument is equal to (HWND)-1, then no dialog should
  944. //      be displayed (the driver is only being queried for support). The
  945. //      driver must still return MMSYSERR_NOERROR (supported) or  
  946. //      MMSYSERR_NOTSUPPORTED (no custom about box supported).
  947. //
  948. //--------------------------------------------------------------------------;
  949.  
  950. LRESULT FNLOCAL acmdDriverAbout
  951. (
  952.     PDRIVERINSTANCE         pdi,
  953.     HWND                    hwnd
  954. )
  955. {
  956.     //
  957.     //  first check to see if we are only being queried for custom about
  958.     //  box support. if hwnd == (HWND)-1 then we are being queried and
  959.     //  should return MMSYSERR_NOTSUPPORTED for 'not supported' and
  960.     //  MMSYSERR_NOERROR for 'supported'.
  961.     //
  962.     if ((HWND)-1 == hwnd)
  963.     {
  964.         //
  965.         //  this ACM driver does NOT support a custom about box, so
  966.         //  return MMSYSERR_NOTSUPPORTED...
  967.         //
  968.         return (MMSYSERR_NOTSUPPORTED);
  969.     }
  970.  
  971.  
  972.     //
  973.     //  this driver does not support a custom dialog, so tell the ACM or
  974.     //  calling application to display one for us. note that this is the
  975.     //  _recommended_ method for consistency and simplicity of ACM drivers.
  976.     //  why write code when you don't have to?
  977.     //
  978.     return (MMSYSERR_NOTSUPPORTED);
  979. } // acmdDriverAbout()
  980.  
  981.  
  982. //==========================================================================;
  983. //
  984. //
  985. //
  986. //
  987. //==========================================================================;
  988.  
  989. //--------------------------------------------------------------------------;
  990. //  
  991. //  LRESULT acmdFormatTagDetails
  992. //  
  993. //  Description:
  994. //      This function handles the ACMDM_FORMATTAG_DETAILS message. This
  995. //      message is normally sent in response to an acmFormatTagDetails or
  996. //      acmFormatTagEnum function call. The purpose of this function is
  997. //      to get details about a specific format tag supported by this ACM
  998. //      driver.
  999. //  
  1000. //  Arguments:
  1001. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  1002. //      This structure is [optionally] allocated during the DRV_OPEN message
  1003. //      which is handled by the acmdDriverOpen function.
  1004. //
  1005. //      LPACMFORMATTAGDETAILS padft: Pointer to an ACMFORMATTAGDETAILS
  1006. //      structure that describes what format tag to retrieve details for.
  1007. //
  1008. //      DWORD fdwDetails: Flags defining what format tag to retrieve the
  1009. //      details for.
  1010. //  
  1011. //  Return (LRESULT):
  1012. //      The return value is zero (MMSYSERR_NOERROR) if this function
  1013. //      succeeds with no errors. The return value is a non-zero error code
  1014. //      if the function fails.
  1015. //
  1016. //      The driver should return MMSYSERR_NOTSUPPORTED if the query type
  1017. //      specified in fdwDetails is not supported. An ACM driver must
  1018. //      support at least the following query types:
  1019. //
  1020. //      ACM_FORMATTAGDETAILSF_INDEX: Indicates that a format tag index
  1021. //      was given in the dwFormatTagIndex member of the ACMFORMATTAGDETAILS
  1022. //      structure. The format tag and details must be returned in the
  1023. //      structure specified by padft. The index ranges from zero to one less
  1024. //      than the cFormatTags member returned in the ACMDRIVERDETAILS
  1025. //      structure for this driver.
  1026. //
  1027. //      ACM_FORMATTAGDETAILSF_FORMATTAG: Indicates that a format tag
  1028. //      was given in the dwFormatTag member of the ACMFORMATTAGDETAILS
  1029. //      structure. The format tag details must be returned in the structure
  1030. //      specified by padft.
  1031. //
  1032. //      ACM_FORMATTAGDETAILSF_LARGESTSIZE: Indicates that the details
  1033. //      on the format tag with the largest format size in bytes must be
  1034. //      returned. The dwFormatTag member will either be WAVE_FORMAT_UNKNOWN
  1035. //      or the format tag to find the largest size for.
  1036. //
  1037. //      If the details for the specified format tag cannot be retrieved
  1038. //      from this driver, then ACMERR_NOTPOSSIBLE should be returned.
  1039. //
  1040. //--------------------------------------------------------------------------;
  1041.  
  1042. LRESULT FNLOCAL acmdFormatTagDetails
  1043. (
  1044.     PDRIVERINSTANCE         pdi,
  1045.     LPACMFORMATTAGDETAILS   padft,
  1046.     DWORD                   fdwDetails
  1047. )
  1048. {
  1049.     UINT                uFormatTag;
  1050.  
  1051.     //
  1052.     //
  1053.     //
  1054.     //
  1055.     //
  1056.     switch (ACM_FORMATTAGDETAILSF_QUERYMASK & fdwDetails)
  1057.     {
  1058.         case ACM_FORMATTAGDETAILSF_INDEX:
  1059.             //
  1060.             //  if the index is too large, then they are asking for a 
  1061.             //  non-existant format.  return error.
  1062.             //
  1063.             if (ACM_DRIVER_MAX_FORMAT_TAGS <= padft->dwFormatTagIndex)
  1064.                 return (ACMERR_NOTPOSSIBLE);
  1065.  
  1066.             uFormatTag = gauFormatTagIndexToTag[(UINT)padft->dwFormatTagIndex];
  1067.             break;
  1068.  
  1069.  
  1070.         case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
  1071.             switch (padft->dwFormatTag)
  1072.             {
  1073.                 case WAVE_FORMAT_UNKNOWN:
  1074.                 case WAVE_FORMAT_PCM:
  1075.                     uFormatTag = WAVE_FORMAT_PCM;
  1076.                     break;
  1077.  
  1078.                 default:
  1079.                     return (ACMERR_NOTPOSSIBLE);
  1080.             }
  1081.             break;
  1082.  
  1083.  
  1084.         case ACM_FORMATTAGDETAILSF_FORMATTAG:
  1085.             if (WAVE_FORMAT_PCM != padft->dwFormatTag)
  1086.                 return (ACMERR_NOTPOSSIBLE);
  1087.  
  1088.             uFormatTag = WAVE_FORMAT_PCM;
  1089.             break;
  1090.  
  1091.  
  1092.         //
  1093.         //  if this ACM driver does not understand a query type, then
  1094.         //  return 'not supported'
  1095.         //
  1096.         default:
  1097.             return (MMSYSERR_NOTSUPPORTED);
  1098.     }
  1099.  
  1100.  
  1101.  
  1102.     //
  1103.     //
  1104.     //
  1105.     //
  1106.     switch (uFormatTag)
  1107.     {
  1108.         case WAVE_FORMAT_PCM:
  1109.             padft->dwFormatTagIndex = 0;
  1110.             padft->dwFormatTag      = WAVE_FORMAT_PCM;
  1111.             padft->cbFormatSize     = sizeof(PCMWAVEFORMAT);
  1112.             padft->fdwSupport       = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  1113.             padft->cStandardFormats = ACM_DRIVER_MAX_STANDARD_FORMATS_PCM;
  1114.  
  1115.             //
  1116.             //  the ACM is responsible for the PCM format tag name
  1117.             //
  1118.             padft->szFormatTag[0]   =  '\0';
  1119.             break;
  1120.  
  1121.         default:
  1122.             return (ACMERR_NOTPOSSIBLE);
  1123.     }
  1124.  
  1125.  
  1126.     //
  1127.     //  return only the requested info
  1128.     //
  1129.     //  the ACM will guarantee that the ACMFORMATTAGDETAILS structure
  1130.     //  passed is at least large enough to hold the base information of
  1131.     //  the details structure
  1132.     //
  1133.     padft->cbStruct = min(padft->cbStruct, sizeof(*padft));
  1134.  
  1135.  
  1136.     //
  1137.     //
  1138.     //
  1139.     return (MMSYSERR_NOERROR);
  1140. } // acmdFormatTagDetails()
  1141.  
  1142.  
  1143. //--------------------------------------------------------------------------;
  1144. //  
  1145. //  LRESULT acmdFormatDetails
  1146. //  
  1147. //  Description:
  1148. //      This function handles the ACMDM_FORMAT_DETAILS message. This
  1149. //      message is normally sent in response to an acmFormatDetails or
  1150. //      acmFormatEnum function call. The purpose of this function is
  1151. //      to get details about a specific format for a specified format tag
  1152. //      supported by this ACM driver.
  1153. //
  1154. //      Note that an ACM driver can return a zero length string for the
  1155. //      format name if it wishes to have the ACM create a format string
  1156. //      for it. This is strongly recommended to simplify internationalizing
  1157. //      the driver--the ACM will automatically take care of that. The
  1158. //      following formula is used to format a string by the ACM:
  1159. //
  1160. //      <nSamplesPerSec> kHz, <bit depth> bit, [Mono | Stereo | nChannels]
  1161. //
  1162. //      <bit depth> = <nAvgBytesPerSec> * 8 / nSamplesPerSec / nChannels;
  1163. //
  1164. //  Arguments:
  1165. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  1166. //      This structure is [optionally] allocated during the DRV_OPEN message
  1167. //      which is handled by the acmdDriverOpen function.
  1168. //
  1169. //      LPACMFORMATDETAILS padf: Pointer to an ACMFORMATDETAILS structure
  1170. //      that describes what format (for a specified format tag) to retrieve
  1171. //      details for.
  1172. //
  1173. //      DWORD fdwDetails: Flags defining what format for a specified format
  1174. //      tag to retrieve the details for.
  1175. //  
  1176. //  Return (LRESULT):
  1177. //      The return value is zero (MMSYSERR_NOERROR) if this function
  1178. //      succeeds with no errors. The return value is a non-zero error code
  1179. //      if the function fails.
  1180. //
  1181. //      The driver should return MMSYSERR_NOTSUPPORTED if the query type
  1182. //      specified in fdwDetails is not supported. An ACM driver must
  1183. //      support at least the following query types:
  1184. //
  1185. //      ACM_FORMATDETAILSF_INDEX: Indicates that a format index for the
  1186. //      format tag was given in the dwFormatIndex member of the
  1187. //      ACMFORMATDETAILS structure. The format details must be returned in
  1188. //      the structure specified by padf. The index ranges from zero to one
  1189. //      less than the cStandardFormats member returned in the
  1190. //      ACMFORMATTAGDETAILS structure for a format tag.
  1191. //
  1192. //      ACM_FORMATDETAILSF_FORMAT: Indicates that a WAVEFORMATEX structure
  1193. //      pointed to by pwfx of the ACMFORMATDETAILS structure was given and
  1194. //      the remaining details should be returned. The dwFormatTag member 
  1195. //      of the ACMFORMATDETAILS will be initialized to the same format
  1196. //      tag as the pwfx member specifies. This query type may be used to
  1197. //      get a string description of an arbitrary format structure.
  1198. //
  1199. //      If the details for the specified format cannot be retrieved
  1200. //      from this driver, then ACMERR_NOTPOSSIBLE should be returned.
  1201. //  
  1202. //--------------------------------------------------------------------------;
  1203.  
  1204. LRESULT FNLOCAL acmdFormatDetails
  1205. (
  1206.     PDRIVERINSTANCE         pdi,
  1207.     LPACMFORMATDETAILS      padf,
  1208.     DWORD                   fdwDetails
  1209. )
  1210. {
  1211.     LPWAVEFORMATEX      pwfx;
  1212.     UINT                uFormatIndex;
  1213.     UINT                u;
  1214.  
  1215.  
  1216.     //
  1217.     //
  1218.     //
  1219.     //
  1220.     //
  1221.     switch (ACM_FORMATDETAILSF_QUERYMASK & fdwDetails)
  1222.     {
  1223.         //
  1224.         //  enumerate by index
  1225.         //
  1226.         //  verify that the format tag is something we know about and
  1227.         //  return the details on the 'standard format' supported by
  1228.         //  this driver at the specified index...
  1229.         //
  1230.         case ACM_FORMATDETAILSF_INDEX:
  1231.             //
  1232.             //  verify that the format tag is something we know about
  1233.             //
  1234.             if (WAVE_FORMAT_PCM != padf->dwFormatTag)
  1235.                 return (ACMERR_NOTPOSSIBLE);
  1236.  
  1237.             if (ACM_DRIVER_MAX_STANDARD_FORMATS_PCM <= padf->dwFormatIndex)
  1238.                 return (ACMERR_NOTPOSSIBLE);
  1239.  
  1240.             //
  1241.             //  put some stuff in more accessible variables--note that we
  1242.             //  bring variable sizes down to a reasonable size for 16 bit
  1243.             //  code...
  1244.             //
  1245.             pwfx = padf->pwfx;
  1246.             uFormatIndex = (UINT)padf->dwFormatIndex;
  1247.  
  1248.             //
  1249.             //  now fill in the format structure
  1250.             //
  1251.             pwfx->wFormatTag      = WAVE_FORMAT_PCM;
  1252.  
  1253.             u = uFormatIndex / (ACM_DRIVER_MAX_BITSPERSAMPLE_PCM * ACM_DRIVER_MAX_CHANNELS);
  1254.             pwfx->nSamplesPerSec  = gauFormatIndexToSampleRate[u]; 
  1255.  
  1256.             u = uFormatIndex % ACM_DRIVER_MAX_CHANNELS;
  1257.             pwfx->nChannels       = u + 1;
  1258.  
  1259.             u = (uFormatIndex / ACM_DRIVER_MAX_CHANNELS) % ACM_DRIVER_MAX_CHANNELS;
  1260.             pwfx->wBitsPerSample  = gauFormatIndexToBitsPerSample[u]; 
  1261.  
  1262.             pwfx->nBlockAlign     = PCM_BLOCKALIGNMENT(pwfx);
  1263.             pwfx->nAvgBytesPerSec = PCM_AVGBYTESPERSEC(pwfx);
  1264.  
  1265.  
  1266.             //
  1267.             //  note that the cbSize field is NOT valid for PCM formats
  1268.             //
  1269.             //  pwfx->cbSize      = 0;
  1270.             break;
  1271.  
  1272.  
  1273.         //
  1274.         //  return details on specified format
  1275.         //
  1276.         //  the caller normally uses this to verify that the format is
  1277.         //  supported and to retrieve a string description...
  1278.         //
  1279.         case ACM_FORMATDETAILSF_FORMAT:
  1280.             if (!pcmIsValidFormat(padf->pwfx))
  1281.                 return (ACMERR_NOTPOSSIBLE);
  1282.  
  1283.             break;
  1284.  
  1285.  
  1286.         default:
  1287.             //
  1288.             //  don't know how to do the query type passed--return 'not
  1289.             //  supported'.
  1290.             //
  1291.             return (MMSYSERR_NOTSUPPORTED);
  1292.     }
  1293.  
  1294.  
  1295.     //
  1296.     //  return the size of the valid information we are returning
  1297.     //
  1298.     //  the ACM will guarantee that the ACMFORMATDETAILS structure
  1299.     //  passed is at least large enough to hold the base structure 
  1300.     //
  1301.     //  note that we let the ACM create the format string for us since
  1302.     //  we require no special formatting (and don't want to deal with 
  1303.     //  internationalization issues, etc). simply set the string to
  1304.     //  a zero length.
  1305.     //
  1306.     padf->cbStruct    = min(padf->cbStruct, sizeof(*padf));
  1307.     padf->fdwSupport  = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  1308.     padf->szFormat[0] = '\0';
  1309.  
  1310.  
  1311.     //
  1312.     //
  1313.     //
  1314.     return (MMSYSERR_NOERROR);
  1315. } // acmdFormatDetails()
  1316.  
  1317.  
  1318. //==========================================================================;
  1319. //
  1320. //
  1321. //
  1322. //
  1323. //==========================================================================;
  1324.  
  1325. //--------------------------------------------------------------------------;
  1326. //  
  1327. //  LRESULT acmdFilterTagDetails
  1328. //  
  1329. //  Description:
  1330. //  
  1331. //  
  1332. //  Arguments:
  1333. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  1334. //      This structure is [optionally] allocated during the DRV_OPEN message
  1335. //      which is handled by the acmdDriverOpen function.
  1336. //  
  1337. //      LPACMFILTERTAGDETAILS padft:
  1338. //  
  1339. //      DWORD fdwDetails:
  1340. //  
  1341. //  Return (LRESULT):
  1342. //  
  1343. //  
  1344. //--------------------------------------------------------------------------;
  1345.  
  1346. LRESULT FNLOCAL acmdFilterTagDetails
  1347. (
  1348.     PDRIVERINSTANCE         pdi,
  1349.     LPACMFILTERTAGDETAILS   padft,
  1350.     DWORD                   fdwDetails
  1351. )
  1352. {
  1353.     UINT                uIds;
  1354.     UINT                uFilterTag;
  1355.  
  1356.     //
  1357.     //
  1358.     //
  1359.     //
  1360.     //
  1361.     switch (ACM_FILTERTAGDETAILSF_QUERYMASK & fdwDetails)
  1362.     {
  1363.         case ACM_FILTERTAGDETAILSF_INDEX:
  1364.             //
  1365.             //  if the index is too large, then they are asking for a 
  1366.             //  non-existant filter.  return error.
  1367.             //
  1368.             if (ACM_DRIVER_MAX_FILTER_TAGS <= padft->dwFilterTagIndex)
  1369.                 return (ACMERR_NOTPOSSIBLE);
  1370.  
  1371.             uFilterTag = (UINT)gadwFilterTagIndexToTag[(UINT)padft->dwFilterTagIndex];
  1372.             break;
  1373.  
  1374.  
  1375.         case ACM_FILTERTAGDETAILSF_LARGESTSIZE:
  1376.             switch (padft->dwFilterTag)
  1377.             {
  1378.                 case WAVE_FILTER_UNKNOWN:
  1379.                 case WAVE_FILTER_ECHO:
  1380.                     uFilterTag = WAVE_FILTER_ECHO;
  1381.                     break;
  1382.  
  1383.                 case WAVE_FILTER_VOLUME:
  1384.                     uFilterTag = WAVE_FILTER_VOLUME;
  1385.                     break;
  1386.  
  1387.                 default:
  1388.                     return (ACMERR_NOTPOSSIBLE);
  1389.             }
  1390.             break;
  1391.  
  1392.  
  1393.         case ACM_FILTERTAGDETAILSF_FILTERTAG:
  1394.             switch (padft->dwFilterTag)
  1395.             {
  1396.                 case WAVE_FILTER_VOLUME:
  1397.                     uFilterTag = WAVE_FILTER_VOLUME;
  1398.                     break;
  1399.  
  1400.                 case WAVE_FILTER_ECHO:
  1401.                     uFilterTag = WAVE_FILTER_ECHO;
  1402.                     break;
  1403.  
  1404.                 default:
  1405.                     return (ACMERR_NOTPOSSIBLE);
  1406.             }
  1407.             break;
  1408.  
  1409.  
  1410.         //
  1411.         //  if this driver does not understand a query type, then
  1412.         //  return 'not supported'
  1413.         //
  1414.         default:
  1415.             return (MMSYSERR_NOTSUPPORTED);
  1416.     }
  1417.  
  1418.  
  1419.  
  1420.     //
  1421.     //
  1422.     //
  1423.     //
  1424.     switch (uFilterTag)
  1425.     {
  1426.         case WAVE_FILTER_VOLUME:
  1427.             padft->dwFilterTagIndex = 0;
  1428.             padft->dwFilterTag      = WAVE_FILTER_VOLUME;
  1429.             padft->cbFilterSize     = sizeof(VOLUMEWAVEFILTER);
  1430.             padft->fdwSupport       = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  1431.             padft->cStandardFilters = ACM_DRIVER_MAX_VOLUME_FILTERS;
  1432.  
  1433.             uIds = IDS_ACM_DRIVER_TAG_NAME_VOLUME;
  1434.             break;
  1435.  
  1436.         case WAVE_FILTER_ECHO:
  1437.             padft->dwFilterTagIndex = 1;
  1438.             padft->dwFilterTag      = WAVE_FILTER_ECHO;
  1439.             padft->cbFilterSize     = sizeof(ECHOWAVEFILTER);
  1440.             padft->fdwSupport       = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  1441.             padft->cStandardFilters = ACM_DRIVER_MAX_ECHO_FILTERS;
  1442.  
  1443.             uIds = IDS_ACM_DRIVER_TAG_NAME_ECHO;
  1444.             break;
  1445.  
  1446.         default:
  1447.             return (ACMERR_NOTPOSSIBLE);
  1448.     }
  1449.  
  1450.  
  1451.     //
  1452.     //  return only the requested info
  1453.     //
  1454.     //  the ACM will guarantee that the ACMFILTERTAGDETAILS structure
  1455.     //  passed is at least large enough to hold the base structure
  1456.     //
  1457.     padft->cbStruct = min(padft->cbStruct, sizeof(*padft));
  1458.  
  1459.     LoadString(ghinst, uIds, padft->szFilterTag, SIZEOF(padft->szFilterTag));
  1460.  
  1461.  
  1462.     //
  1463.     //
  1464.     //
  1465.     return (MMSYSERR_NOERROR);
  1466. } // acmdFilterTagDetails()
  1467.  
  1468.  
  1469. //--------------------------------------------------------------------------;
  1470. //  
  1471. //  UINT acmdFilterDetailsToString
  1472. //  
  1473. //  Description:
  1474. //  
  1475. //  
  1476. //  Arguments:
  1477. //      LPWAVEFILTER pwf:
  1478. //  
  1479. //      LPTSTR szFilter:
  1480. //  
  1481. //  Return (UINT):
  1482. //  
  1483. //  
  1484. //--------------------------------------------------------------------------;
  1485.  
  1486. UINT FNLOCAL acmdFilterDetailsToString
  1487. (
  1488.     LPWAVEFILTER            pwf,
  1489.     LPTSTR                  szFilter
  1490. )
  1491. {
  1492.     UINT                u;
  1493.     TCHAR               ach[ACMFILTERDETAILS_FILTER_CHARS];
  1494.     LPVOLUMEWAVEFILTER  pwfVol;
  1495.     LPECHOWAVEFILTER    pwfEcho;
  1496.  
  1497.  
  1498.     if( !szFilter ) {
  1499.         return 0L;
  1500.     }
  1501.     
  1502.     *szFilter = '\0';
  1503.     if (volumeIsValidFilter(pwf))
  1504.     {
  1505.         pwfVol = (LPVOLUMEWAVEFILTER)pwf;
  1506.         LoadString(ghinst, IDS_ACM_DRIVER_FORMAT_VOLUME, ach, SIZEOF(ach));
  1507.         u = wsprintf( szFilter, ach,
  1508.                  (WORD)(((pwfVol->dwVolume * 100) / 0x10000)) );
  1509.         return( u );
  1510.     }
  1511.     else if (echoIsValidFilter(pwf))
  1512.     {
  1513.         pwfEcho = (LPECHOWAVEFILTER)pwf;
  1514.         LoadString(ghinst, IDS_ACM_DRIVER_FORMAT_ECHO, ach, SIZEOF(ach));
  1515.         u = wsprintf( szFilter, ach,
  1516.                  (WORD)(((pwfEcho->dwVolume * 100) / 0x10000)),
  1517.                      (WORD)pwfEcho->dwDelay );
  1518.         return( u );
  1519.     }
  1520.     return ( 0 );
  1521. } // acmdFilterDetailsToString()
  1522.  
  1523.  
  1524.  
  1525. //--------------------------------------------------------------------------;
  1526. //  
  1527. //  LRESULT acmdFilterDetails
  1528. //  
  1529. //  Description:
  1530. //  
  1531. //  
  1532. //  Arguments:
  1533. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  1534. //      This structure is [optionally] allocated during the DRV_OPEN message
  1535. //      which is handled by the acmdDriverOpen function.
  1536. //
  1537. //      LPACMFILTERDETAILS padf:
  1538. //
  1539. //      DWORD fdwDetails:
  1540. //  
  1541. //  Return (LRESULT):
  1542. //
  1543. //
  1544. //--------------------------------------------------------------------------;
  1545.  
  1546. LRESULT FNLOCAL acmdFilterDetails
  1547. (
  1548.     PDRIVERINSTANCE         pdi,
  1549.     LPACMFILTERDETAILS      padf,
  1550.     DWORD                   fdwDetails
  1551. )
  1552. {
  1553.     UINT                uFilterIndex;
  1554.     LPWAVEFILTER        pwf;
  1555.     LPVOLUMEWAVEFILTER  pwfVolume;
  1556.     LPECHOWAVEFILTER    pwfEcho;
  1557.     UINT                u;
  1558.  
  1559.     pwf = padf->pwfltr;
  1560.  
  1561.     //
  1562.     //
  1563.     //
  1564.     //
  1565.     //
  1566.     switch (ACM_FILTERDETAILSF_QUERYMASK & fdwDetails)
  1567.     {
  1568.         case ACM_FILTERDETAILSF_INDEX:
  1569.             //
  1570.             //  enumerate by index
  1571.             //
  1572.             //  for this converter, this is more code than necessary... just
  1573.             //  verify that the filter tag is something we know about
  1574.             //
  1575.             switch (padf->dwFilterTag)
  1576.             {
  1577.                 case WAVE_FILTER_VOLUME:
  1578.                     if (ACM_DRIVER_MAX_VOLUME_FILTERS <= padf->dwFilterIndex)
  1579.                         return (ACMERR_NOTPOSSIBLE);
  1580.  
  1581.                     pwfVolume        = (LPVOLUMEWAVEFILTER)padf->pwfltr;
  1582.                     pwf->cbStruct    = sizeof(VOLUMEWAVEFILTER);
  1583.                     pwf->dwFilterTag = WAVE_FILTER_VOLUME;
  1584.                     pwf->fdwFilter   = 0;
  1585.                     pwfVolume->dwVolume = gdwFilterIndexToVolume[(UINT)padf->dwFilterIndex];
  1586.                     break;
  1587.  
  1588.                 case WAVE_FILTER_ECHO:
  1589.                     if (ACM_DRIVER_MAX_ECHO_FILTERS <= padf->dwFilterIndex)
  1590.                         return (ACMERR_NOTPOSSIBLE);
  1591.  
  1592.                     pwfEcho             = (LPECHOWAVEFILTER)padf->pwfltr;
  1593.                     pwf->cbStruct       = sizeof(ECHOWAVEFILTER);
  1594.                     pwf->dwFilterTag    = WAVE_FILTER_ECHO;
  1595.                     pwf->fdwFilter      = 0;
  1596.  
  1597.                     uFilterIndex = (UINT)padf->dwFilterIndex;
  1598.  
  1599.                     u = uFilterIndex / ACM_DRIVER_NUM_DELAY;
  1600.                     pwfEcho->dwVolume = gdwFilterIndexToEchoVol[u]; 
  1601.  
  1602.                     u = uFilterIndex % ACM_DRIVER_NUM_DELAY;
  1603.                     pwfEcho->dwDelay = gdwFilterIndexToDelay[u]; 
  1604.                     break;
  1605.  
  1606.                 default:
  1607.                     return (ACMERR_NOTPOSSIBLE);
  1608.             }
  1609.  
  1610.         case ACM_FILTERDETAILSF_FILTER:
  1611.             //
  1612.             //  must want to verify that the filter passed in is supported
  1613.             //  and return a string description...
  1614.             //
  1615.             switch (pwf->dwFilterTag)
  1616.             {
  1617.                 case WAVE_FILTER_VOLUME:
  1618.                     if (!volumeIsValidFilter(pwf))
  1619.                         return (ACMERR_NOTPOSSIBLE);
  1620.  
  1621.                     break;
  1622.  
  1623.                 case WAVE_FILTER_ECHO:
  1624.                     if (!echoIsValidFilter(pwf))
  1625.                         return (ACMERR_NOTPOSSIBLE);
  1626.  
  1627.                     break;
  1628.  
  1629.                 default:
  1630.                     return (ACMERR_NOTPOSSIBLE);
  1631.             }
  1632.             break;
  1633.  
  1634.  
  1635.         default:
  1636.             //
  1637.             //  don't know how to do the query type passed--return 'not
  1638.             //  supported'.
  1639.             //
  1640.             return (MMSYSERR_NOTSUPPORTED);
  1641.     }
  1642.     
  1643.  
  1644.     //
  1645.     //  return only the requested info
  1646.     //
  1647.     //  the ACM will guarantee that the ACMFILTERDETAILS structure
  1648.     //  passed is at least large enough to hold everything in the base
  1649.     //  filter details structure...
  1650.     //
  1651.     //  get a nice friendly string for the filter we made
  1652.     //
  1653.     acmdFilterDetailsToString(pwf, padf->szFilter);
  1654.  
  1655.  
  1656.     //
  1657.     //  if they asked for more info than we know how to return, then
  1658.     //  set size of valid structure bytes to correct value.
  1659.     //
  1660.     padf->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_FILTER;
  1661.     padf->cbStruct   = min(padf->cbStruct, sizeof(*padf));
  1662.  
  1663.     //
  1664.     //
  1665.     //
  1666.     return (MMSYSERR_NOERROR);
  1667. } // acmdFilterDetails()
  1668.  
  1669.  
  1670.  
  1671. //==========================================================================;
  1672. //
  1673. //
  1674. //
  1675. //
  1676. //==========================================================================;
  1677.  
  1678. //--------------------------------------------------------------------------;
  1679. //
  1680. //  LRESULT acmdStreamOpen
  1681. //
  1682. //  Description:
  1683. //      This function handles the ACMDM_STREAM_OPEN message. This message
  1684. //      is sent to initiate a new conversion stream. This is usually caused
  1685. //      by an application calling acmStreamOpen. If this function is
  1686. //      successful, then one or more ACMDM_STREAM_CONVERT messages will be
  1687. //      sent to convert individual buffers (user calls acmStreamConvert).
  1688. //
  1689. //      Note that an ACM driver will not receive open requests for ASYNC
  1690. //      or FILTER operations unless the ACMDRIVERDETAILS_SUPPORTF_ASYNC
  1691. //      or ACMDRIVERDETAILS_SUPPORTF_FILTER flags are set in the 
  1692. //      ACMDRIVERDETAILS structure. There is no need for the driver to
  1693. //      check for these requests unless it sets those support bits.
  1694. //
  1695. //      If the ACM_STREAMOPENF_QUERY flag is set in the padsi->fdwOpen
  1696. //      member, then no resources should be allocated. Just verify that
  1697. //      the conversion request is possible by this driver and return the
  1698. //      appropriate error (either ACMERR_NOTPOSSIBLE or MMSYSERR_NOERROR).
  1699. //      The driver will NOT receive an ACMDM_STREAM_CLOSE for queries.
  1700. //
  1701. //      If the ACM_STREAMOPENF_NONREALTIME bit is NOT set, then conversion
  1702. //      must be done in 'real-time'. This is a tough one to describe
  1703. //      exactly. If the driver may have trouble doing the conversion without
  1704. //      breaking up the audio, then a configuration dialog might be used
  1705. //      to allow the user to specify whether the real-time conversion 
  1706. //      request should be succeeded. DO NOT SUCCEED THE CALL UNLESS YOU
  1707. //      ACTUALLY CAN DO REAL-TIME CONVERSIONS! There may be another driver
  1708. //      installed that can--so if you succeed the call you are hindering
  1709. //      the performance of the user's system! Don't be foolish.
  1710. //
  1711. //  Arguments:
  1712. //      PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
  1713. //      This structure is [optionally] allocated during the DRV_OPEN message
  1714. //      which is handled by the acmdDriverOpen function.
  1715. //
  1716. //      LPACMDRVSTREAMINSTANCE padsi: Pointer to instance data for the
  1717. //      conversion stream. This structure was allocated by the ACM and
  1718. //      filled with the most common instance data needed for conversions.
  1719. //      This structure will be passed back to all future stream messages
  1720. //      if the open succeeds. The information in this structure will never
  1721. //      change during the lifetime of the stream--so it is not necessary
  1722. //      to re-verify the information referenced by this structure.
  1723. //
  1724. //  Return (LRESULT):
  1725. //      The return value is zero (MMSYSERR_NOERROR) if this function
  1726. //      succeeds with no errors. The return value is a non-zero error code
  1727. //      if the function fails.
  1728. //
  1729. //      A driver should return ACMERR_NOTPOSSIBLE if the conversion cannot
  1730. //      be performed due to incompatible source and destination formats.
  1731. //
  1732. //      A driver should return MMSYSERR_NOTSUPPORTED if the conversion
  1733. //      cannot be performed in real-time and the request does not specify
  1734. //      the ACM_STREAMOPENF_NONREALTIME flag.
  1735. //
  1736. //--------------------------------------------------------------------------;
  1737.  
  1738. LRESULT FNLOCAL acmdStreamOpen
  1739. (
  1740.     PDRIVERINSTANCE         pdi,
  1741.     LPACMDRVSTREAMINSTANCE  padsi
  1742. )
  1743. {
  1744.     PSTREAMINSTANCE     psi;
  1745.     LPWAVEFORMATEX      pwfxSrc;
  1746.     LPWAVEFORMATEX      pwfxDst;
  1747.     LPWAVEFILTER        pwfltr;
  1748.     BOOL                fRealTime;
  1749.  
  1750.  
  1751.     //
  1752.     //
  1753.     //
  1754.     pwfxSrc = padsi->pwfxSrc;
  1755.     pwfxDst = padsi->pwfxDst;
  1756.     pwfltr  = padsi->pwfltr;
  1757.  
  1758.     fRealTime = (0 == (padsi->fdwOpen & ACM_STREAMOPENF_NONREALTIME));
  1759.  
  1760.     if( fRealTime )
  1761.     {
  1762.     //
  1763.         //  We only do non-realtime conversions.
  1764.     //  Return failure if we are asked for realtime.
  1765.         //
  1766.         return (ACMERR_NOTPOSSIBLE);
  1767.     }
  1768.  
  1769.     
  1770.     //
  1771.     //  the most important condition to check before doing anything else
  1772.     //  is that this ACM driver can actually perform the conversion we are
  1773.     //  being opened for. this check should fail as quickly as possible
  1774.     //  if the conversion is not possible by this driver.
  1775.     //
  1776.     //  it is VERY important to fail quickly so the ACM can attempt to 
  1777.     //  find a driver that is suitable for the conversion. also note that
  1778.     //  the ACM may call this driver several times with slightly different
  1779.     //  format specifications before giving up.
  1780.     //
  1781.     //  this driver first verifies that the source and destination formats
  1782.     //  are acceptable...
  1783.     //
  1784.     //  NOTE! for a 'filter only' driver, you only need to check one
  1785.     //  of the formats. the ACM will have already verified that the source
  1786.     //  and destination formats are equal. so if one is acceptable to this
  1787.     //  driver, they both are.
  1788.     //
  1789.     if (!pcmIsValidFormat(pwfxSrc))
  1790.     {
  1791.         //
  1792.         //  either the source or destination format is illegal for this
  1793.         //  driver--or the conversion between the formats can not be 
  1794.         //  performed by this driver.
  1795.         //
  1796.         return (ACMERR_NOTPOSSIBLE);
  1797.     }
  1798.  
  1799.  
  1800.     //
  1801.     //  now verify the filter..
  1802.     //
  1803.     if (!volumeIsValidFilter(pwfltr) && !echoIsValidFilter(pwfltr))
  1804.     {
  1805.         return (ACMERR_NOTPOSSIBLE);
  1806.     }
  1807.  
  1808.  
  1809.  
  1810.     //
  1811.     //  we have determined that the conversion requested is possible by
  1812.     //  this driver. now check if we are just being queried for support.
  1813.     //  if this is just a query, then do NOT allocate any instance data
  1814.     //  or create tables, etc. just succeed the call.
  1815.     //
  1816.     if (0 != (ACM_STREAMOPENF_QUERY & padsi->fdwOpen))
  1817.     {
  1818.         return (MMSYSERR_NOERROR);
  1819.     }
  1820.  
  1821.  
  1822.     //
  1823.     //  we have decided that this driver can handle the conversion stream.
  1824.     //  so we want to do _AS MUCH WORK AS POSSIBLE_ right now to prepare
  1825.     //  for converting data. any resource allocation, table building, etc
  1826.     //  that can be dealt with at this time should be done.
  1827.     //
  1828.     //  THIS IS VERY IMPORTANT! all ACMDM_STREAM_CONVERT messages need to
  1829.     //  be handled as quickly as possible.
  1830.     //
  1831.     //
  1832.  
  1833.     //
  1834.     //  we have decided that this driver can handle the conversion stream.
  1835.     //  so we want to do _AS MUCH WORK AS POSSIBLE_ right now to prepare
  1836.     //  for converting data. any resource allocation, table building, etc
  1837.     //  that can be dealt with at this time should be done.
  1838.     //
  1839.     //  THIS IS VERY IMPORTANT! all ACMDM_STREAM_CONVERT messages need to
  1840.     //  be handled as quickly as possible.
  1841.     //
  1842.     //  this driver allocates a small instance structure for each stream
  1843.     //
  1844.     //
  1845.     psi = (PSTREAMINSTANCE)LocalAlloc(LPTR, sizeof(*psi));
  1846.     if (NULL == psi)
  1847.     {
  1848.         return (MMSYSERR_NOMEM);
  1849.     }
  1850.  
  1851.  
  1852.     //
  1853.     //  fill out our instance structure
  1854.     //
  1855.     //  this driver stores a pointer to the conversion function that will
  1856.     //  be used for each conversion on this stream. we also store a 
  1857.     //  copy of the _current_ configuration of the driver instance we
  1858.     //  are opened on. this must not change during the life of the stream
  1859.     //  instance.
  1860.     //
  1861.     //  this is also very important! if the user is able to configure how
  1862.     //  the driver performs conversions, the changes should only affect
  1863.     //  future open streams. all current open streams should behave as
  1864.     //  they were configured during the open.
  1865.     //
  1866.  
  1867.     //
  1868.     //
  1869.     //
  1870.     //
  1871.     if (WAVE_FILTER_VOLUME == pwfltr->dwFilterTag)
  1872.     {
  1873.         psi->fnConvert      = msfilterVolume;
  1874.         psi->fdwConfig      = pdi->fdwConfig;
  1875.         psi->hpbHistory     = NULL;
  1876.         psi->dwPlace        = 0L;
  1877.         psi->dwHistoryDone  = 0L;
  1878.     }
  1879.     else
  1880.     {
  1881.         LPECHOWAVEFILTER    pwfEcho;
  1882.         DWORD               cb;
  1883.         LPBYTE              pb;
  1884.  
  1885.         psi->fnConvert      = msfilterEcho;
  1886.         psi->fdwConfig      = pdi->fdwConfig;
  1887.         psi->hpbHistory     = NULL;
  1888.         psi->dwPlace        = 0L;
  1889.         psi->dwHistoryDone  = 0L;
  1890.  
  1891.         pwfEcho = (LPECHOWAVEFILTER)pwfltr;
  1892.  
  1893.         //
  1894.         //  compute size of delay buffer--add 4 because we're lame!
  1895.         //
  1896.         cb  = (pwfxSrc->nSamplesPerSec * pwfEcho->dwDelay / 1000) *
  1897.           pwfxSrc->nBlockAlign;
  1898.         cb += 4;
  1899.  
  1900.         pb = (LPBYTE)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, cb);
  1901.         if (NULL == pb)
  1902.         {
  1903.             //
  1904.             //  free the stream instance structure and fail with no memory
  1905.             //
  1906.             LocalFree((HLOCAL)psi);
  1907.             return (MMSYSERR_NOMEM);
  1908.         }
  1909.  
  1910.         psi->hpbHistory = (BYTE _huge *)pb;
  1911.     }
  1912.  
  1913.  
  1914.     //
  1915.     //  fill in our instance data--this will be passed back to all stream
  1916.     //  messages in the ACMDRVSTREAMINSTANCE structure. it is entirely
  1917.     //  up to the driver what gets stored (and maintained) in the
  1918.     //  fdwDriver and dwDriver members.
  1919.     //
  1920.     padsi->fdwDriver = 0L;
  1921.     padsi->dwDriver  = (DWORD)(UINT)psi;
  1922.  
  1923.     return (MMSYSERR_NOERROR);
  1924. } // acmdStreamOpen()
  1925.  
  1926.  
  1927. //--------------------------------------------------------------------------;
  1928. //
  1929. //  LRESULT acmdStreamClose
  1930. //
  1931. //  Description:
  1932. //      This function is called to handle the ACMDM_STREAM_CLOSE message.
  1933. //      This message is sent when a conversion stream is no longer being
  1934. //      used (the stream is being closed; usually by an application
  1935. //      calling acmStreamClose). The ACM driver should clean up any resources
  1936. //      that were allocated for the stream.
  1937. //
  1938. //  Arguments:
  1939. //      LPACMDRVSTREAMINSTANCE padsi: Pointer to instance data for the
  1940. //      conversion stream. This structure was allocated by the ACM and
  1941. //      filled with the most common instance data needed for conversions.
  1942. //      The information in this structure is exactly the same as it was
  1943. //      during the ACMDM_STREAM_OPEN message--so it is not necessary
  1944. //      to re-verify the information referenced by this structure.
  1945. //
  1946. //  Return (LRESULT):
  1947. //      The return value is zero (MMSYSERR_NOERROR) if this function
  1948. //      succeeds with no errors. The return value is a non-zero error code
  1949. //      if the function fails.
  1950. //
  1951. //      NOTE! It is _strongly_ recommended that a driver not fail to close
  1952. //      a conversion stream.
  1953. //
  1954. //      An asyncronous conversion stream may fail with ACMERR_BUSY if there
  1955. //      are pending buffers. An application may call acmStreamReset to
  1956. //      force all pending buffers to be posted.
  1957. //
  1958. //--------------------------------------------------------------------------;
  1959.  
  1960. LRESULT FNLOCAL acmdStreamClose
  1961. (
  1962.     LPACMDRVSTREAMINSTANCE  padsi
  1963. )
  1964. {
  1965.     PSTREAMINSTANCE     psi;
  1966.  
  1967.     //
  1968.     //  the driver should clean up all privately allocated resources that
  1969.     //  were created for maintaining the stream instance. if no resources
  1970.     //  were allocated, then simply succeed.
  1971.     //
  1972.     //  in the case of this driver, we need to free the stream instance
  1973.     //  structure that we allocated during acmdStreamOpen.
  1974.     //
  1975.     psi = (PSTREAMINSTANCE)(UINT)padsi->dwDriver;
  1976.     if (NULL != psi)
  1977.     {
  1978.         //
  1979.         //  free up  the delay buffer if one was allocated (will be for
  1980.         //  the echo filter
  1981.         //
  1982.         if (NULL != psi->hpbHistory)
  1983.         {
  1984.             GlobalFreePtr(psi->hpbHistory);
  1985.         }
  1986.  
  1987.         //
  1988.         //  free the stream instance structure
  1989.         //
  1990.         LocalFree((HLOCAL)psi);
  1991.     }
  1992.     
  1993.     return (MMSYSERR_NOERROR);
  1994. } // acmdStreamClose()
  1995.  
  1996.  
  1997. //--------------------------------------------------------------------------;
  1998. //  
  1999. //  LRESULT acmdStreamSize
  2000. //  
  2001. //  Description:
  2002. //      This function handles the ACMDM_STREAM_SIZE message. The purpose
  2003. //      of this function is to provide the _largest size in bytes_ that
  2004. //      the source or destination buffer needs to be given the input and
  2005. //      output formats and the size in bytes of the source or destination
  2006. //      data buffer.
  2007. //
  2008. //      In other words: how big does my destination buffer need to be to
  2009. //      hold the converted data? (ACM_STREAMSIZEF_SOURCE)
  2010. //
  2011. //      Or: how big can my source buffer be given the destination buffer?
  2012. //      (ACM_STREAMSIZEF_DESTINATION)
  2013. //
  2014. //  Arguments:
  2015. //      LPACMDRVSTREAMINSTANCE padsi: Pointer to instance data for the
  2016. //      conversion stream. This structure was allocated by the ACM and
  2017. //      filled with the most common instance data needed for conversions.
  2018. //      The information in this structure is exactly the same as it was
  2019. //      during the ACMDM_STREAM_OPEN message--so it is not necessary
  2020. //      to re-verify the information referenced by this structure.
  2021. //  
  2022. //      LPACMDRVSTREAMSIZE padss: Specifies a pointer to the ACMDRVSTREAMSIZE
  2023. //      structure that defines the conversion stream size query attributes.
  2024. //  
  2025. //  Return (LRESULT):
  2026. //      The return value is zero (MMSYSERR_NOERROR) if this function
  2027. //      succeeds with no errors. The return value is a non-zero error code
  2028. //      if the function fails.
  2029. //  
  2030. //      An ACM driver should return MMSYSERR_NOTSUPPORTED if a query type
  2031. //      is requested that the driver does not understand. Note that a driver
  2032. //      must support both the ACM_STREAMSIZEF_DESTINATION and
  2033. //      ACM_STREAMSIZEF_SOURCE queries.
  2034. //
  2035. //      If the conversion would be 'out of range' given the input arguments,
  2036. //      then ACMERR_NOTPOSSIBLE should be returned.
  2037. //
  2038. //--------------------------------------------------------------------------;
  2039.  
  2040. LRESULT FNLOCAL acmdStreamSize
  2041. (
  2042.     LPACMDRVSTREAMINSTANCE  padsi,
  2043.     LPACMDRVSTREAMSIZE      padss
  2044. )
  2045. {
  2046.     LPWAVEFORMATEX      pwfxSrc;
  2047.     LPWAVEFORMATEX      pwfxDst;
  2048.     LPWAVEFILTER        pwfltr;
  2049.     DWORD               dw;
  2050.  
  2051.  
  2052.     pwfxSrc = padsi->pwfxSrc;
  2053.     pwfxDst = padsi->pwfxDst;
  2054.     pwfltr  = padsi->pwfltr; 
  2055.  
  2056.  
  2057.     //
  2058.     //
  2059.     //
  2060.     switch (ACM_STREAMSIZEF_QUERYMASK & padss->fdwSize)
  2061.     {
  2062.         case ACM_STREAMSIZEF_SOURCE:
  2063.             dw = padss->cbSrcLength;
  2064.  
  2065.             if( pwfltr->dwFilterTag == WAVE_FILTER_VOLUME )
  2066.             {
  2067.                 //
  2068.                 //  Source and dest sizes are the same for volume.
  2069.                 //
  2070.                 //  block align the destination
  2071.                 //
  2072.                 dw = PCM_BYTESTOSAMPLES(pwfxSrc, padss->cbSrcLength);
  2073.                 dw = PCM_SAMPLESTOBYTES(pwfxSrc, dw);
  2074.             }
  2075.             else if( pwfltr->dwFilterTag == WAVE_FILTER_ECHO )
  2076.             {
  2077.                 dw = PCM_BYTESTOSAMPLES(pwfxSrc, padss->cbSrcLength);
  2078.                 dw = PCM_SAMPLESTOBYTES(pwfxSrc, dw);
  2079.             }
  2080.  
  2081.             if (0 == dw)
  2082.             {
  2083.                 return (ACMERR_NOTPOSSIBLE);
  2084.             }
  2085.  
  2086.             padss->cbDstLength = dw;
  2087.             return (MMSYSERR_NOERROR);
  2088.  
  2089.  
  2090.         case ACM_STREAMSIZEF_DESTINATION:
  2091.             dw = padss->cbDstLength;
  2092.  
  2093.             if( pwfltr->dwFilterTag == WAVE_FILTER_VOLUME )
  2094.             {
  2095.                 //
  2096.                 //  Source and dest sizes are the same for volume.
  2097.                 //
  2098.                 //  block align the destination
  2099.                 //
  2100.                 dw = PCM_BYTESTOSAMPLES(pwfxDst, padss->cbDstLength);
  2101.                 dw = PCM_SAMPLESTOBYTES(pwfxDst, dw);
  2102.             }
  2103.             else if( pwfltr->dwFilterTag == WAVE_FILTER_ECHO )
  2104.             {
  2105.                 dw = PCM_BYTESTOSAMPLES(pwfxDst, padss->cbDstLength);
  2106.                 dw = PCM_SAMPLESTOBYTES(pwfxDst, dw);
  2107.             }
  2108.  
  2109.             if (0 == dw)
  2110.             {
  2111.                 return (ACMERR_NOTPOSSIBLE);
  2112.             }
  2113.  
  2114.             padss->cbSrcLength = dw;
  2115.             return (MMSYSERR_NOERROR);
  2116.     }
  2117.  
  2118.     //
  2119.     //
  2120.     //
  2121.     return (MMSYSERR_NOTSUPPORTED);
  2122. } // acmdStreamSize()
  2123.  
  2124.  
  2125. //==========================================================================;
  2126. //
  2127. //
  2128. //
  2129. //
  2130. //==========================================================================;
  2131.  
  2132. //--------------------------------------------------------------------------;
  2133. //
  2134. //  LRESULT DriverProc
  2135. //
  2136. //  Description:
  2137. //      
  2138. //
  2139. //  Arguments:
  2140. //      DWORD dwId: For most messages, dwId is the DWORD value that
  2141. //      the driver returns in response to a DRV_OPEN message. Each time
  2142. //      the driver is opened, through the OpenDriver API, the driver
  2143. //      receives a DRV_OPEN message and can return an arbitrary, non-zero
  2144. //      value. The installable driver interface saves this value and returns
  2145. //      a unique driver handle to the application. Whenever the application
  2146. //      sends a message to the driver using the driver handle, the interface
  2147. //      routes the message to this entry point and passes the corresponding
  2148. //      dwId. This mechanism allows the driver to use the same or different
  2149. //      identifiers for multiple opens but ensures that driver handles are
  2150. //      unique at the application interface layer.
  2151. //
  2152. //      The following messages are not related to a particular open instance
  2153. //      of the driver. For these messages, the dwId will always be zero.
  2154. //
  2155. //          DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
  2156. //
  2157. //      HDRVR hdrvr: This is the handle returned to the application 
  2158. //      by the driver interface.
  2159. //
  2160. //      UINT uMsg: The requested action to be performed. Message
  2161. //      values below DRV_RESERVED are used for globally defined messages.
  2162. //      Message values from DRV_RESERVED to DRV_USER are used for defined
  2163. //      driver protocols. Messages above DRV_USER are used for driver
  2164. //      specific messages.
  2165. //
  2166. //      LPARAM lParam1: Data for this message. Defined separately for
  2167. //      each message.
  2168. //
  2169. //      LPARAM lParam2: Data for this message. Defined separately for
  2170. //      each message.
  2171. //
  2172. //  Return (LRESULT):
  2173. //      Defined separately for each message. 
  2174. //
  2175. //--------------------------------------------------------------------------;
  2176.  
  2177. EXTERN_C LRESULT FNEXPORT DriverProc
  2178. (
  2179.     DWORD                   dwId, 
  2180.     HDRVR                   hdrvr,
  2181.     UINT                    uMsg,
  2182.     LPARAM                  lParam1,
  2183.     LPARAM                  lParam2
  2184. )
  2185. {
  2186.     LRESULT             lr;
  2187.     PDRIVERINSTANCE     pdi;
  2188.  
  2189.     //
  2190.     //  make pdi either NULL or a valid instance pointer. note that dwId
  2191.     //  is 0 for several of the DRV_* messages (ie DRV_LOAD, DRV_OPEN...)
  2192.     //  see acmdDriverOpen for information on what dwId is for other
  2193.     //  messages (instance data).
  2194.     //
  2195.     pdi = (PDRIVERINSTANCE)(UINT)dwId;
  2196.  
  2197.     switch (uMsg)
  2198.     {
  2199.         //
  2200.         //  lParam1: Unused.
  2201.         //
  2202.         //  lParam2: Unused.
  2203.         //
  2204.         case DRV_LOAD:
  2205.         case DRV_FREE:
  2206.             return (1L);
  2207.  
  2208.         
  2209.         //
  2210.         //  lParam1: Not used. Ignore this argument.
  2211.         //
  2212.         //  lParam2: Pointer to ACMDRVOPENDESC (or NULL).
  2213.         //
  2214.         case DRV_OPEN:
  2215.             lr = acmdDriverOpen(hdrvr, (LPACMDRVOPENDESC)lParam2);
  2216.             return (lr);
  2217.  
  2218.         //
  2219.         //  lParam1: Unused.
  2220.         //
  2221.         //  lParam2: Unused.
  2222.         //
  2223.         case DRV_CLOSE:
  2224.             lr = acmdDriverClose(pdi);
  2225.             return (lr);
  2226.  
  2227.         //
  2228.         //  lParam1: Unused.
  2229.         //
  2230.         //  lParam2: Unused.
  2231.         //
  2232.         case DRV_INSTALL:
  2233.             return ((LRESULT)DRVCNF_RESTART);
  2234.  
  2235.         //
  2236.         //  lParam1: Unused.
  2237.         //
  2238.         //  lParam2: Unused.
  2239.         //
  2240.         case DRV_REMOVE:
  2241.             return ((LRESULT)DRVCNF_RESTART);
  2242.  
  2243.             
  2244.  
  2245.         //
  2246.         //  lParam1: Not used.
  2247.         //
  2248.         //  lParam2: Not used.
  2249.         //
  2250.         case DRV_QUERYCONFIGURE:
  2251.             //
  2252.             //  set up lParam1 and lParam2 to values that can be used by
  2253.             //  acmdDriverConfigure to know that it is being 'queried'
  2254.             //  for configuration support.
  2255.             //
  2256.             lParam1 = -1L;
  2257.             lParam2 = 0L;
  2258.  
  2259.             //--fall through--//
  2260.  
  2261.         //
  2262.         //  lParam1: Handle to parent window for the configuration dialog
  2263.         //           box.
  2264.         //
  2265.         //  lParam2: Optional pointer to DRVCONFIGINFO structure.
  2266.         //
  2267.         case DRV_CONFIGURE:
  2268.             lr = acmdDriverConfigure(pdi, (HWND)lParam1, (LPDRVCONFIGINFO)lParam2);
  2269.             return (lr);
  2270.  
  2271.  
  2272.         //
  2273.         //  lParam1: Pointer to ACMDRIVERDETAILS structure.
  2274.         //
  2275.         //  lParam2: Size in bytes of ACMDRIVERDETAILS stucture passed.
  2276.         //
  2277.         case ACMDM_DRIVER_DETAILS:
  2278.             lr = acmdDriverDetails(pdi, (LPACMDRIVERDETAILS)lParam1);
  2279.             return (lr);
  2280.  
  2281.         //
  2282.         //  lParam1: Handle to parent window to use if displaying your own
  2283.         //           about box.
  2284.         //
  2285.         //  lParam2: Not used.
  2286.         //
  2287.         case ACMDM_DRIVER_ABOUT:
  2288.             lr = acmdDriverAbout(pdi, (HWND)lParam1);
  2289.             return (lr);
  2290.  
  2291. //--------------------------------------------------------------------------;
  2292. //--------------------------------------------------------------------------;
  2293.  
  2294.         //
  2295.         //  lParam1: Pointer to FORMATTAGDETAILS structure.
  2296.         //
  2297.         //  lParam2: fdwDetails
  2298.         //
  2299.         case ACMDM_FORMATTAG_DETAILS:
  2300.             lr = acmdFormatTagDetails(pdi, (LPACMFORMATTAGDETAILS)lParam1, lParam2);
  2301.             return (lr);
  2302.  
  2303.             
  2304.         //
  2305.         //  lParam1: Pointer to FORMATDETAILS structure.
  2306.         //
  2307.         //  lParam2: fdwDetails
  2308.         //
  2309.         case ACMDM_FORMAT_DETAILS:
  2310.             lr = acmdFormatDetails(pdi, (LPACMFORMATDETAILS)lParam1, lParam2);
  2311.             return (lr);
  2312.  
  2313. //--------------------------------------------------------------------------;
  2314. //--------------------------------------------------------------------------;
  2315.  
  2316.         //
  2317.         //  lParam1: FILTERTAGDETAILS 
  2318.         //
  2319.         //  lParam2: fdwDetails
  2320.         //
  2321.         case ACMDM_FILTERTAG_DETAILS:
  2322.             lr = acmdFilterTagDetails(pdi, (LPACMFILTERTAGDETAILS)lParam1, lParam2);
  2323.             return (lr);
  2324.  
  2325.         //
  2326.         //  lParam1: Pointer to the details structure.
  2327.         //
  2328.         //  lParam2: fdwDetails
  2329.         //
  2330.         case ACMDM_FILTER_DETAILS:
  2331.             lr = acmdFilterDetails(pdi, (LPACMFILTERDETAILS)lParam1, lParam2);
  2332.             return (lr);
  2333.  
  2334. //--------------------------------------------------------------------------;
  2335. //--------------------------------------------------------------------------;
  2336.             
  2337.         //
  2338.         //  lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
  2339.         //
  2340.         //  lParam2: Not used.
  2341.         //
  2342.         case ACMDM_STREAM_OPEN:
  2343.             lr = acmdStreamOpen(pdi, (LPACMDRVSTREAMINSTANCE)lParam1);
  2344.             return (lr);
  2345.  
  2346.         //
  2347.         //  lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
  2348.         //
  2349.         //  lParam2: Not Used.
  2350.         //
  2351.         case ACMDM_STREAM_CLOSE:
  2352.             lr = acmdStreamClose((LPACMDRVSTREAMINSTANCE)lParam1);
  2353.             return (lr);
  2354.  
  2355.         //
  2356.         //  lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
  2357.         //
  2358.         //  lParam2: Pointer to ACMDRVSTREAMSIZE structure.
  2359.         //
  2360.         case ACMDM_STREAM_SIZE:
  2361.             lr = acmdStreamSize((LPACMDRVSTREAMINSTANCE)lParam1, (LPACMDRVSTREAMSIZE)lParam2);
  2362.             return (lr);
  2363.  
  2364.         //
  2365.         //  lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
  2366.         //
  2367.         //  lParam2: Pointer to ACMDRVSTREAMHEADER structure.
  2368.         //
  2369.         case ACMDM_STREAM_CONVERT:
  2370.         {
  2371.             PSTREAMINSTANCE         psi;
  2372.             LPACMDRVSTREAMINSTANCE  padsi;
  2373.             LPACMDRVSTREAMHEADER    padsh;
  2374.  
  2375.             //
  2376.             //  our stream instance data is a pointer to the conversion
  2377.             //  procedure needed to convert the pwfxSrc data to pwfxDst.
  2378.             //  the correct procedure to use was decided in acmdStreamOpen
  2379.             //
  2380.             padsi = (LPACMDRVSTREAMINSTANCE)lParam1;
  2381.             padsh = (LPACMDRVSTREAMHEADER)lParam2;
  2382.             
  2383.             psi   = (PSTREAMINSTANCE)(UINT)padsi->dwDriver;
  2384.  
  2385.             lr = psi->fnConvert(padsi, padsh);
  2386.             return (lr);
  2387.         }
  2388.     }
  2389.  
  2390.     //
  2391.     //  if we are executing the following code, then this ACM driver does not
  2392.     //  handle the message that was sent. there are two ranges of messages
  2393.     //  we need to deal with:
  2394.     //
  2395.     //  o   ACM specific driver messages: if an ACM driver does not answer a
  2396.     //      message sent in the ACM driver message range, then it must
  2397.     //      return MMSYSERR_NOTSUPPORTED. this applies to the 'user'
  2398.     //      range as well (for consistency).
  2399.     //
  2400.     //  o   other installable driver messages: if an ACM driver does not
  2401.     //      answer a message that is NOT in the ACM driver message range,
  2402.     //      then it must call DefDriverProc and return that result.
  2403.     //      the exception to this is ACM driver procedures installed as
  2404.     //      ACM_DRIVERADDF_FUNCTION through acmDriverAdd. in this case,
  2405.     //      the driver procedure should conform to the ACMDRIVERPROC
  2406.     //      prototype and also return zero instead of calling DefDriverProc.
  2407.     //
  2408.     if (uMsg >= ACMDM_USER)
  2409.         return (MMSYSERR_NOTSUPPORTED);
  2410.     else
  2411.         return (DefDriverProc(dwId, hdrvr, uMsg, lParam1, lParam2));
  2412. } // DriverProc()
  2413.